<?php


namespace core\proxy;


use core\utils\Bean;
use core\utils\BeanManager;
use core\utils\LateInitBean;
use Exception;
use ReflectionClass;

class ProxyEnhancer {
    private ?Bean $bean = null;
    private ?ProxyEnhancer $next = null;
    private ReflectionClass $beanReflectionClass;
    private ?DaemonProxy $proxy;
    private string $clazz;

    /**
     * ProxyEnhancer constructor.
     * @param ProxyEnhancer $next
     * @param ReflectionClass $beanReflectionClass
     * @param DaemonProxy $proxy
     * @param string $clazz
     * @throws \ReflectionException
     */
    public function __construct(?ProxyEnhancer $next, ReflectionClass $beanReflectionClass, ?DaemonProxy $proxy, string $clazz)
    {
        $this->next = $next;
        $this->beanReflectionClass = $beanReflectionClass;
        $this->proxy = $proxy;
        $this->clazz = $clazz;
        // 如果是普通的Bean(非LateInitBean)的话，那就直接初始化这个Bean。
        if ($beanReflectionClass->isSubclassOf(Bean::class) && !$beanReflectionClass->isSubclassOf(LateInitBean::class)) {
            $this->invokeInstance();
        }
    }

    public function setNext(ProxyEnhancer $next) {
        $this->next = $next;
    }


    public function __call($name, $arguments) {
        $object = null;
        $method = $this->beanReflectionClass->getMethod($name);
        try {
            $list = $this;
            while (null != $list && null != $list->proxy) {
                if ($list->proxy->matchMethod($method)) {
                    $list->proxy->doBefore($arguments, $this->beanReflectionClass, $method);
                }
                $list = $list->next;
            }

            if (null == $this->bean) {
                $this->invokeInstance();
            }

            $o = $method->invokeArgs($this->bean, $arguments);
            $list = $this;

            while (null != $list && null != $list->proxy) {
                if ($list->proxy->matchMethod($method)) {
                    $list->proxy->doBeforeReturn($o);
                }
                $list = $list->next;
            }

            // 方法不存在的时候直接抛出一个异常
            return $o;
        } catch (Exception $e) {
            $list = $this;
            while (null != $list && null != $list->proxy) {
                if ($list->proxy->matchMethod($method)) {
                    $list->proxy->doThrow($e);
                }
                $list = $list->next;
            }

            throw $e;
        } finally {
            $list = $this;
            while (null != $list && null != $list->proxy) {
                if ($list->proxy->matchMethod($method)) {
                    $list->proxy->doAfter();
                }
                $list = $list->next;
            }
        }
    }

    /**
     * @throws \ReflectionException | Exception
     */
    private function invokeInstance() {
        if ($this->beanReflectionClass->isSubclassOf(Bean::class)) {
            if ($this->beanReflectionClass->hasMethod('getInstance')) {
                $method = $this->beanReflectionClass->getMethod('getInstance');
                $this->bean = $method->invokeArgs(null, $this->getParams($method));
            } else {
                $this->bean = $this->beanReflectionClass->newInstanceArgs($this->getParams($this->beanReflectionClass->getConstructor()));
            }
        } else {
            throw new Exception("非法的Bean");
        }
    }

    private function getParams(\ReflectionMethod $method = null) : array {
        if (null == $method) {
            return [];
        }
        $params = $method->getParameters();
        $rev = [];
        foreach ($params as $param) {
            if ($param->getClass()->isSubclassOf(Bean::class)) {
                $rev[] = BeanManager::getInstance()->getBean($param->getClass()->getName());
            } else {
                throw new \Exception("非法的bean");
            }
        }
        return $rev;
    }

    public function jsonSerialize() {
        return '{}';
    }
}